package shark.oss.impl;

import io.minio.*;
import io.minio.errors.*;
import org.springframework.util.Assert;
import shark.oss.annotation.OssType;
import shark.oss.exception.OssException;
import shark.oss.model.FileUrl;
import shark.oss.model.ServerDefine;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.UUID;

@OssType("minio")
public class MinIoOssFileImpl extends OssFileServiceImpl {
    private MinioClient client;
    private String bucketName;
    private String endpoint;
    private String accessKey;
    private String secretKey;

    public MinIoOssFileImpl(ServerDefine serverDefine) throws OssException {
        super(serverDefine);
        Map<String,String> params = serverDefine.getParams();
        Assert.notEmpty(params,"MinIO服务器访问参数不可为null");

        bucketName = params.get("bucket");
        endpoint = params.get("endpoint");
        accessKey = params.get("accessKey");
        secretKey = params.get("secretKey");

        Assert.hasText(bucketName,"远程附件服务器参数初始化失败，bucket=null。服务名=" + getServerName());
        Assert.hasText(endpoint,"远程附件服务器参数初始化失败，host=null。服务名=" + getServerName());
        Assert.hasText(accessKey,"远程附件服务器参数初始化失败，accessKey=null。服务名=" + getServerName());

        try {
            client = MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
            if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception ex){
            throw new OssException("创建MinIO client失败",ex);
        }
    }

    @Override
    public String persist(String server, InputStream inStream, String path, String fileName) throws OssException {
        Assert.hasText(path, "path参数不能为空!");

        if(fileName==null){
            fileName = UUID.randomUUID().toString();
        }
        saveObject(path,fileName,inStream);

        return generateFileUrl(this.getServerName(), path, fileName);
    }

    @Override
    public void update(String fileUrl, InputStream inStream) throws OssException {
        //解析fileUrl
        FileUrl reqUrl = parseFileUrl(fileUrl);

        String path = reqUrl.getFilePath(); 		//文件路径
        String fileName = reqUrl.getFileId(); 		//文件名称

        saveObject(path,fileName,inStream);
    }

    @Override
    public void download(String fileUrl, HttpServletRequest request, HttpServletResponse response) throws OssException {
        FileUrl reqUrl = parseFileUrl(fileUrl);
        String objectName = parseObjectName(reqUrl);

        InputStream inputStream = null;
        try {
            inputStream = client.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            download(inputStream, reqUrl.getFileId(), request, response);
        }
        catch (Exception ex){
            throw new OssException("从MinIO服务器中获取文件失败。文件名 = " + objectName, ex);
        } finally {
            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    @Override
    public void delete(String fileUrl) throws OssException {
        String objectName = parseObjectName(parseFileUrl(fileUrl));
        try {
            client.removeObject(
                    RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
        catch (Exception ex){
            throw new OssException("删除附件失败，附件地址=" + fileUrl,ex);
        }
    }

    @Override
    public boolean isExist(String fileUrl) throws OssException, ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String objectName = parseObjectName(parseFileUrl(fileUrl));

        StatObjectResponse objectResponse = client.statObject(
                StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        if (objectResponse == null) {
            return false;
        }
        return true;
    }

    @Override
    public InputStream getFile(String fileUrl) throws OssException {
        FileUrl reqUrl = parseFileUrl(fileUrl);
        String objectName = parseObjectName(reqUrl);

        InputStream inputStream = null;
        try {
            inputStream = client.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
        }
        catch (Exception ex){
            throw new OssException("从MinIO服务器中获取文件失败。文件名 = " + objectName, ex);
        }
        return inputStream;
    }

    private void saveObject(String path,String fileName,InputStream inStream)
            throws OssException{
        StringBuilder fullFilename = new StringBuilder();
        fullFilename.append(path);
        if(!path.endsWith("/")){
            fullFilename.append("/");
        }
        fullFilename.append(fileName);
        try {
            client.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fullFilename.toString())
                    .stream(inStream, -1, 10485760)
                    .build());
        } catch (Exception ex){
            throw new OssException("向MinIO服务器存储目标文件失败",ex);
        }
    }

    private String parseObjectName(FileUrl fileUrl){
        String filePath = fileUrl.getFilePath();
        String fileName = fileUrl.getFileId();
        StringBuilder fullFilename = new StringBuilder();
        fullFilename.append(filePath);
        if(!filePath.endsWith("/")){
            fullFilename.append("/");
        }
        fullFilename.append(fileName);
        return fullFilename.toString();
    }
}
